#include "conductor.h"
#include "dlist.h"

#define priority(thread) (thread->priority)

/* active thread */
static mx_thread_pt activetd = mx_null;
/* ready list */
static mx_thread_pt readylist = mx_null;

mx_inline static mx_usize_t readylist_count ()
{
    mx_usize_t count = 0;
    if (readylist != mx_null)
    {
        count = mx_dlist_count(&readylist->list);
    }
    return count;
}

mx_inline static mx_usize_t sameprio_count (mx_thread_pt pthread)
{
    mx_usize_t count = 1;
    /* prev */
    for (mx_thread_pt pprev = mx_thread_prev(pthread);
         priority(pprev) == priority(pthread);
         pprev = mx_thread_prev(pprev))
    {
        if (pprev == pthread)
        {
            return count;
        }
        count ++;
    }
    /* next */
    for (mx_thread_pt pnext = mx_thread_next(pthread);
         priority(pnext) == priority(pthread);
         pnext = mx_thread_next(pnext))
    {
        count ++;
    }
    return count;
}

mx_inline static mx_thread_pt sameprio_first (mx_thread_pt pthread)
{
    mx_thread_pt ret = pthread;
    for (mx_thread_pt pprev = mx_thread_prev(pthread);
         pprev != pthread && priority(pprev) == priority(pthread);
         pprev = mx_thread_prev(pprev))
    {
        ret = pprev;
    }
    return ret;
}

mx_inline static mx_thread_pt sameprio_last (mx_thread_pt pthread)
{
    mx_thread_pt ret = pthread;
    for (mx_thread_pt pnext = mx_thread_next(pthread);
         pnext != pthread && priority(pnext) == priority(pthread);
         pnext = mx_thread_next(pnext))
    {
        ret = pnext;
    }
    return ret;
}

mx_inline static mx_thread_pt sameprio_next (mx_thread_pt pthread)
{
    mx_thread_pt next = mx_thread_next(pthread);
    if (priority(next) != priority(pthread))
    {
        return sameprio_first(pthread);
    }
    return next;
}

mx_inline static mx_thread_pt sameprio_prev (mx_thread_pt pthread)
{
    mx_thread_pt prev = mx_thread_prev(pthread);
    if (priority(prev) != priority(pthread))
    {
        return sameprio_last(pthread);
    }
    return prev;
}

mx_inline static void readylist_insert (mx_thread_pt pthread)
{
    if (readylist == mx_null)
    {
        readylist = pthread;
    }
    else
    {
        if (priority(mx_thread_prev(readylist)) < priority(pthread))
        {
            mx_dlist_insert_before(&readylist->list, &pthread->list);
        }
        else
        {
            mx_dlist_foreach(&readylist->list, l, {
                mx_thread_pt th = mx_thread_container(l, list);
                if (priority(pthread) <= priority(th))
                {
                    mx_dlist_insert_before(&th->list, &pthread->list);
                    if (th == readylist)
                    {
                        readylist = pthread;
                    }
                    break;
                }
            });
        }
    }
}

mx_inline static void readylist_remove (mx_thread_pt pthread)
{
    if (readylist == pthread)
    {
        mx_thread_pt next = mx_thread_next(pthread);
        if (next != pthread)
        {
            readylist = next;
        }
        else
        {
            readylist = mx_null;
        }
    }
    mx_dlist_remove(&pthread->list);
}

void mx_conductor_add (mx_thread_pt pthread)
{
    /* do nothing */
}

void mx_conductor_remove (mx_thread_pt pthread)
{
    /* do nothing */
}

void mx_conductor_chstat (mx_thread_pt pthread, mx_thread_status_t status)
{
    switch (status)
    {
    case MX_THREAD_STATUS_INIT:
        pthread->status = MX_THREAD_STATUS_INIT;
        break;
    case MX_THREAD_STATUS_READY:
        if (pthread->status != MX_THREAD_STATUS_RUNING)
        {
            /* remove from list (e.g. suspend list) */
            mx_dlist_remove(&pthread->list);
            readylist_insert(pthread);
        }
        pthread->status = MX_THREAD_STATUS_READY;
        break;
    case MX_THREAD_STATUS_BLOCK:
        readylist_remove(pthread);
        pthread->status = MX_THREAD_STATUS_BLOCK;
        break;
    case MX_THREAD_STATUS_RUNING:
        pthread->status = MX_THREAD_STATUS_RUNING;
        break;
    case MX_THREAD_STATUS_DEAD:
        readylist_remove(pthread);
        pthread->status = MX_THREAD_STATUS_DEAD;
        break;
    
    default:
        break;
    }
}

mx_thread_pt mx_conductor_highest (void)
{
    mx_thread_pt ret;

    if (activetd == mx_null ||
        (activetd->status != MX_THREAD_STATUS_READY && activetd->status != MX_THREAD_STATUS_RUNING))
    {
        ret = readylist;
    }
    else
    {
        if (priority(readylist) < priority(activetd))
        {
            ret = readylist;
        }
        else if (priority(readylist) == priority(activetd))
        {
            if (mx_timer_isenable(&activetd->timer))
            {
                ret = activetd;
            }
            else
            {
                ret = sameprio_next(activetd);
            }
        }
        else
        {
            ret = activetd;
        }
    }
    return ret;
}

mx_bool_t mx_conductor_hascrew(mx_thread_pt pthread)
{
    if (sameprio_count(pthread) > 1)
    {
        return mx_true;
    }
    else
    {
        return mx_false;
    }
}

mx_thread_pt mx_conductor_active (void)
{
    return activetd;
}

void mx_conductor_chactive (mx_thread_pt pthread)
{
    activetd = pthread;
}
